#include "myjassvm.h"
#include "Extern_global_Variable.h"

enum TScriptDataContainerType {ctVariable, ctRegister, ctStackItem};

TTransformJassCodeHandleFunction pTransformJassCodeHandle;

// FIXME: game uses _InitializedType when setting variable value

// procedure OnScriptCreated(hScript: TScriptHandle);
// begin

// end;

// procedure OnScriptDestroyed(hScript: TScriptHandle);
// begin

// end;

procedure DebugProcess(TProcessData Process)
{
	// for i = 0 to Process.Natives.Count do begin

	// end;
};

procedure FillProcess(TProcessData Process, PInstance Instance)
{
	PJassThreadLocal Environment;
	PJassScript Script;
	PHashedStringTable Strings;
	PChar Key;
	Dword Hash;
	PNativeFunc Native;
	PFuncAddr Func;
	Dword vvi;

	Environment = GetJassThreadLocal();
	Script = Instance->Script;
	Strings = Script->Strings;

	Process.Globals.SetSize(Strings->FItems.GetLength());
	Process.Natives.SetSize(Strings->FItems.GetLength());
	Process.Functions.SetSize(Strings->FItems.GetLength());
	Process.NativesData.SetSize(Strings->FItems.GetLength());

	for (vvi = 1; vvi <= Strings->FItems.GetLength() -1; vvi++)
	{
		Key = Strings->FItems.GetItem(vvi)->HashKeyData.Key;
		Hash = Strings->FItems.GetItem(vvi)->HashKeyData.Hash;

		if (Environment->Natives.Find(Key, Hash, Native))
		{
			Process.Natives.SetValue(vvi,Native);
			Process.NativesData.SetValue(vvi,TNativeData::TNativeData(*Native));
		};

		if (Instance->NameToFuncAddr->Find(Key, Hash, Func))
		{
			Process.Functions.SetValue(vvi,Func);
		};
	};

	Process.VarChangeObservers.SetSize(Strings->FItems.GetLength());

	DebugProcess(Process);
};

// Copies all the items from a script string table to a instance string table,
// that must be empty so handles in both tables will be the same.
procedure SetupStringTable(PInstance pInstance)
{
	Integer vvi;
	Dword hString;

		// null item already present so skip him
		for (vvi = 1; vvi <= (*pInstance).Script->Strings->GetLength() - 1; vvi++)
		{
			hString = pInstance->StringTable.GetHandle(pInstance->Script->Strings->GetItem(vvi));
			pInstance->StringTable.GetItem(hString).FReferences;
		};
};

procedure DummyVariableChangedCallback(
	Pointer Dummy,
	Dword NewValue,
	PChar Name,
	Pointer Parameter
) // fastcall
{

};

procedure __stdcall DummyManageHandleFunction(Pointer Parameter) // fastcall
{

};

procedure OnInstanceCreated(TInstanceHandle hInstance)
{
	PInstance Instance;
	TProcessData* Process;
	TThreadData* Thread;
	TThreadData* ParentThread;

	#ifdef DEBUG
	char vStr[100];
	sprintf(vStr,"Instance %d created.",hInstance);
	MessageBoxA(NULL,vStr,"Send!",MB_OK | MB_ICONWARNING);
	DebugLn(vStr);
	#endif
	Instance = GetJassThreadLocal()->Instances.GetItem(hInstance);

	if (!Instance->IsChild)
	{
		Instance->VarChangedCallback = &DummyVariableChangedCallback;
		Instance->RefHandleCallback = &DummyManageHandleFunction;
		SetupStringTable(Instance);
		Process = new TProcessData(Instance);
		FillProcess(*Process, Instance);
		JassEnvironment.Processes->Add(*Process);
	}
	else
	{
		ParentThread = new TThreadData((TProcessData*) JassEnvironment.ThreadTable->GetData(Instance->ParentHandle) );
		Process = &ParentThread->FProcess;
	};

	Thread = new TThreadData(Process);
	JassEnvironment.ThreadTable->InsertValue(hInstance, Pointer(Thread));
	#ifdef DEBUG
	char vStr2[100];
	sprintf(vStr2,"Instance %d processed.",hInstance);
	MessageBoxA(NULL,vStr2,"Send!",MB_OK | MB_ICONWARNING);
	DebugLn(vStr2);
	#endif
};

Boolean IsStringExist(PInstance Instance, TJassStringHandle StringHandle)
{
	Boolean Result;
	TProcessData* Process;
	TThreadData* TThread;
	String _s;

	if (StringHandle < Instance->Script->Strings->GetLength())
	{
		Result = TRUE;
	}
	else
	{
		TThread = new TThreadData((TProcessData*)JassEnvironment.ThreadTable->GetData(Instance->Handle));
		Process = &TThread->FProcess;
		Result = Process->MyStringStorage.GetValue(StringHandle, _s);
	};
	return Result;
};

procedure OnStringDestroyed(PInstance Instance, TJassStringHandle StringHandle)
{
	TProcessData Process(Instance);

	//Process = TThreadData(JassEnvironment.ThreadTable[Instance->Handle]).FProcess;

	#ifdef DEBUG
	if (StringHandle < Instance->Script->Strings->Length)
	{
		char vStr[100];
		sprintf(vStr,"Warning! Destroying string from a script table. %d ""%s"".",StringHandle,Process.MyStringStorage[StringHandle]);
		MessageBoxA(NULL,vStr,"Send!",MB_OK | MB_ICONWARNING);
		DebugLn(vStr);
	};
	#endif

	Process.MyStringStorage.FreeHandle(StringHandle);
};

procedure DecrementStringReferences(PInstance Instance, TJassStringHandle StringHandle)
{
	if (StringHandle != 0)
	{
		#ifdef DEBUG
		if (!IsStringExist(Instance, StringHandle))
		{
			char vStr[100];
			sprintf(vStr,"Attempt to decrement references of a freed string %d.",StringHandle);
			MessageBoxA(NULL,vStr,"Error!",MB_OK | MB_ICONWARNING);
		};
		#endif

		Instance->DecrementStringReferences(StringHandle);
		if (Instance->StringTable.GetItem(StringHandle).FReferences == 0)
		{
			OnStringDestroyed(Instance, StringHandle);
		}
	};
};

procedure IncrementStringReferences(PInstance Instance, TJassStringHandle StringHandle)
{
	#ifdef DEBUG
	if (StringHandle != 0)
	{
		if (!IsStringExist(Instance, StringHandle))
		{
			char vStr[100];
			sprintf(vStr,"Attempt to increment references of a freed string %d.",StringHandle);
			MessageBoxA(NULL,vStr,"Error!",MB_OK | MB_ICONWARNING);
		};
	};
	#endif

	Instance->IncrementStringReferences(StringHandle);
};

TJassStringHandle GetStringHandle(PInstance Instance, PChar Value)
{
	TJassStringHandle Result;
	Boolean Created;
	TProcessData* Process;
	String ValueCopy;

	if (Value != NULL)
	{
		ValueCopy = String(Value);

		Result = Instance->StringTable.GetHandle(PChar(ValueCopy), Created);

		if (Created)
		{
			Process = (TProcessData*)JassEnvironment.ThreadTable->GetData(Instance->Handle);
			Process->MyStringStorage.GetValue(Result,ValueCopy);
		};

		IncrementStringReferences(Instance, Result);
	}
	else
	{
		Result = 0;
	};
	return Result;
};

PJassString GetStringObject(PInstance Instance, TJassStringHandle StringHandle)
{
	#ifdef DEBUG
	if (!IsStringExist(Instance, StringHandle))
	{
		char vStr[100];
		sprintf(vStr,"Attempt to get content of a freed string %d.",StringHandle);
		MessageBoxA(NULL,vStr,"Error!",MB_OK | MB_ICONWARNING);
	};
	#endif

	return Instance->StringTable.GetItemPointer(StringHandle);
};

PChar GetStringContent(PInstance Instance, TJassStringHandle StringHandle)
{
	return GetStringObject(Instance, StringHandle)->GetContent();
};

procedure DecrementValueReferences(PInstance Instance, Dword Value, TScriptDataType ValueType, TScriptDataContainerType TargetContainerType)
{
	Boolean IsVariable;

	IsVariable = !((TargetContainerType == ctRegister) || (TargetContainerType == ctStackItem));

	switch (ValueType)
	{
		case stString:
			DecrementStringReferences(Instance, Value);
			break;
		case stHandle:
			#ifdef VANULLLA_COMPATIBILITY
			if (IsVariable)
			#endif
			Instance->DecrementHandleReferences(Value);
			break;
	};
};

procedure IncrementValueReferences(PInstance Instance, Dword Value, TScriptDataType ValueType, TScriptDataContainerType TargetContainerType)
{
	Boolean IsVariable;

	IsVariable = !((TargetContainerType == ctRegister) || (TargetContainerType == ctStackItem));

	switch (ValueType)
	{
		case stString:
			IncrementStringReferences(Instance, Value);
			break;
		case stHandle:
			#ifdef VANULLLA_COMPATIBILITY
			if (IsVariable)
			#endif
			Instance->IncrementHandleReferences(Value);
			break;
	};
};

procedure ChangeValue(PInstance Instance, Dword Value, Dword NewValue, TScriptDataType ValueType, TScriptDataContainerType TargetContainerType)
{
	IncrementValueReferences(Instance, NewValue, ValueType, TargetContainerType);
	DecrementValueReferences(Instance, Value, ValueType, TargetContainerType);
	Value = NewValue;
};

procedure DecrementScriptDataValueReferences(PInstance Instance, TScriptData Data, TScriptDataContainerType TargetContainerType)
{
	DecrementValueReferences(Instance, Data.Value, Data._Type, TargetContainerType);
};

procedure IncrementScriptDataValueReferences(PInstance Instance, TScriptData Data, TScriptDataContainerType TargetContainerType)
{
	IncrementValueReferences(Instance, Data.Value, Data._Type, TargetContainerType);
};

procedure FreeInstanceArray(PInstance Instance, TScriptData Data)
{
	PJassArray JassArray;
	Integer vvi;
	TThreadData* Thread;

	JassArray = PJassArray(Data.Value);

	switch (Data.InitializedType)
	{
		case stArrayOfString:
			{
				for (vvi = 0; vvi <= JassArray->List.GetLength() - 1; vvi++)
				{
					DecrementStringReferences(Instance, JassArray->List.GetItem(vvi));
				};
			};
			break;

		#ifndef VANULLLA_COMPATIBILITY
		case stArrayOfHandle:
			{
				for (vvi = 0; vvi <= JassArray->List.GetLength() - 1; vvi++)
				{
					Instance->DecrementHandleReferences(JassArray->List.GetItem(vvi));
				};
			};
			break;
		#endif
	};

	Thread = new TThreadData((TProcessData*)JassEnvironment.ThreadTable->GetData(Instance->Handle));
	Thread->FProcess.JassArrayPool.FreeItem(JassArray);
};

procedure ClearScriptData(PInstance Instance, TScriptData Data, TScriptDataContainerType DestinationContainerType)
{
	if ((DestinationContainerType == ctVariable)
		&& (Data.InitializedType >= stArrayOfInteger))
	{
		FreeInstanceArray(Instance, Data);
	}
	else
	{
		DecrementScriptDataValueReferences(Instance, Data, DestinationContainerType);
	};

	Data.Value = 0;
	Data._Type = stVoid;

	#ifndef SET_ALL_TYPE_FIELDS
	if (DestinationContainerType == ctVariable)
	#endif
	Data.InitializedType = stVoid;
};

procedure SetScriptDataValue(PInstance Instance, TScriptData* AData, Dword ANewValue, TScriptDataType ANewType, TScriptDataContainerType DestinationContainerType)
{
	// Increment before decrement in case if Source = Destination
	// to prevent object destruction
	IncrementValueReferences(Instance, ANewValue, ANewType, DestinationContainerType);
	DecrementScriptDataValueReferences(Instance, *AData, DestinationContainerType);

	AData->Value = ANewValue;

	#ifndef SET_ALL_TYPE_FIELDS
	if (DestinationContainerType != ctVariable)
	#endif
	AData->_Type = ANewType;

	#ifndef SET_ALL_TYPE_FIELDS
	if (DestinationContainerType == ctVariable)
	#endif
	AData->InitializedType = ANewType;
};

procedure InitScriptDataValue(PInstance Instance, TScriptData AData, Dword ANewValue, TScriptDataType ANewType, TScriptDataContainerType DestinationContainerType)
{
	IncrementValueReferences(Instance, ANewValue, ANewType, DestinationContainerType);

	AData.Value = ANewValue;

	#ifndef SET_ALL_TYPE_FIELDS
	if (DestinationContainerType != ctVariable)
	#endif
	AData._Type = ANewType;

	#ifndef SET_ALL_TYPE_FIELDS
	if (DestinationContainerType == ctVariable)
	#endif
	AData.InitializedType = ANewType;
};

procedure CopyScriptData(PInstance Instance, const TScriptData *Source, TScriptData *Destination, TScriptDataContainerType DestinationContainerType)
{
	SetScriptDataValue(
		Instance,
		Destination,
		Source->Value,
		Source->_Type,
		DestinationContainerType
	);
};

procedure MoveScriptData(PInstance Instance, TScriptData Source, TScriptData Destination, TScriptDataContainerType DestinationContainerType)
{
	DecrementScriptDataValueReferences(
		Instance,
		Destination,
		DestinationContainerType
	);

	Destination.Value = Source.Value;
	Destination._Type = Source._Type;

	#ifndef SET_ALL_TYPE_FIELDS
	if (DestinationContainerType == ctVariable)
	{
	#endif
	Destination.InitializedType = Source._Type;
	#ifndef SET_ALL_TYPE_FIELDS
	};
	#endif

	if (DestinationContainerType != ctStackItem)
	{
		Source.Value = 0;
		Source._Type = stVoid;
	};
};

procedure CleanupInstanceFrames(PInstance pInstance)
{
	PFrame Frame;
	TThreadData* Thread;

	Thread = new TThreadData((TProcessData*)JassEnvironment.ThreadTable->GetData(pInstance->Handle));

	while (TRUE)
	{
		Frame = pInstance->Frames.GetFirst();
		if (Frame == NULL)
			break;

		pInstance->Frames.Remove(Frame);
		Thread->FProcess.FramePool.FreeItem(Frame);
	};

	Thread->ClearFrames();
};

procedure ClearInstanceRegisters(PInstance Instance)
{
	Integer vvi;

	for (vvi = 0; vvi <= sizeof(*Instance->Registers)/sizeof(Instance->Registers[0]) - 1; vvi++)
	{
		ClearScriptData(Instance, Instance->Registers[vvi], ctRegister);
	};
};

procedure OnInstanceDestroying(TInstanceHandle hInstance)
{
	PInstance Instance;
	TProcessData Process;
	TThreadData* Thread;

	#ifdef DEBUG
	char vStr[100];
	sprintf(vStr,"Destroying instance %d.",hInstance);
	MessageBoxA(NULL,vStr,"Send!",MB_OK | MB_ICONWARNING);
	DebugLn(vStr);
	#endif
	Instance = GetJassThreadLocal()->Instances.GetItem(hInstance);

	Thread = new TThreadData((TProcessData*) JassEnvironment.ThreadTable->GetData(hInstance));
	Process = Thread->FProcess;

	CleanupInstanceFrames(Instance);
	ClearInstanceRegisters(Instance);
	JassEnvironment.ThreadTable->FreeHandle(hInstance);
	Thread->~TThreadData();
	
	if (Process.Threads->vCount == 0)
	{
		Instance->Globals->Clear();
		JassEnvironment.Processes->Remove(Process);
		Process.~TProcessData();
	};
	#ifdef DEBUG
	char vStr2[100];
	sprintf(vStr2,"Instance %d data cleared.",hInstance);
	MessageBoxA(NULL,vStr2,"Send!",MB_OK | MB_ICONWARNING);
	DebugLn(vStr2);
	#endif
};


	typedef PHashedString* PPHashedString;

	struct TOpDataPack
	{
		PInstance Instance;
		PInstruction Instruction;

		PFrame Frame;
		TInstance::PVariables Locals;
		TInstance::PVariables Globals;
		PPHashedString ScriptStrings;

		PInstruction Code;
		TInstance::PRegisters Registers;

		TProcessData Process;
		TThreadData* Thread;
		TVariablesTable FastGlobals;
		TVariablesTable FastLocals;
		TFunctionsDataTable FastFunctions;
		TNativesDataTable FastNatives;
		TNativeExtraDataTable FastNativesExtra;
		TVarChangeObserversTable VarChangeObservers;

		TScriptExitCode ExitCode;

	public:
		TOpDataPack() : Instance(0)
		{
			Thread = new TThreadData(NULL);
		};
	};

	enum TVariableScope {vsGlobal, vsLocal};
	
PScriptData GetVariable(TJassStringHandle hName, TOpDataPack Params, TVariableScope &VarScope) 
{
	PScriptData Result;
		if (Params.FastLocals.GetValue2(hName, Result))
		{
			VarScope = vsLocal;
		}
		else
		{
			VarScope = vsGlobal;
			Result = Params.FastGlobals.GetValue(hName);
		};
	return Result;
};


PScriptData GetVariable(TJassStringHandle hName, const TOpDataPack Params)
{
	TVariableScope Unused;

	return GetVariable(hName, Params, Unused);
};

procedure PutVariableToMap
(
	TJassStringHandle hName,
	PScriptData Variable,
	TInstance::PVariables Map,
	PPHashedString Strings 
)
{
		Map->FastPut(Strings[hName]->HashKeyData.Key, Strings[hName]->HashKeyData.Hash, Variable);
};

PScriptData DefineVariable
(
	TOpDataPack Params,
	TJassStringHandle hName,
	TScriptDataType VarType,
	TVariableScope VarScope 
)
{
	PScriptData Result;
	TVariablesTable FastTable;
	#ifdef PROFILER_COMPATIBILITY
	TInstance::PVariables SlowTable;
	#endif
	Result = Params.Process.ScriptDataPool.GetItem();
	Result->_Type = VarType;

	if (Integer(VarType) >= Integer(stArrayOfInteger))
	{
		Result->InitializedType = VarType;
	};

	switch (VarScope)
	{
		case vsGlobal:
			{
			FastTable = Params.FastGlobals;
			#ifdef PROFILER_COMPATIBILITY
			Params.SlowTable = Params.Globals;
			#endif
			};
			break;

		case vsLocal:
			{
			FastTable = Params.FastLocals;
			#ifdef PROFILER_COMPATIBILITY
			Params.SlowTable = Params.Locals;
			#endif
			};
	};

	if (VarType >= stArrayOfInteger)
	{
		Result->Value = (Dword)Params.Process.JassArrayPool.GetItem();
	};

	FastTable.SetValue(hName,Result);
	#ifdef PROFILER_COMPATIBILITY
	PutVariableToMap(hName, Result, Params.SlowTable, Params.ScriptStrings);
	#endif
	return Result;
};

procedure StartFrame(TOpDataPack Params)
{
		Params.Frame = Params.Process.FramePool.GetItem();
		Params.Instance->Frames.Prepend(Params.Frame);
		Params.Locals = &Params.Frame->Locals;

		if (Params.Thread->Frames.GetLast() != NULL)
		{
			Params.Thread->Frames.GetLast()->Locals = Params.FastLocals;
		};
		Params.Thread->Frames.Add(Params.Process.FrameDataPool.GetItem());
		Params.FastLocals = Params.Thread->Frames.GetLast()->Locals;
};

procedure OnFrameChanged(TOpDataPack Params)
{
		Params.Frame = Params.Instance->Frames.GetFirst();

		if (Params.Frame != NULL)
		{
			Params.Locals = &Params.Frame->Locals;
		};

		if (Params.Thread->Frames.GetLast() != NULL)
		{
			Params.FastLocals = Params.Thread->Frames.GetLast()->Locals;
		};
};

procedure EndFrame(TOpDataPack Params)
{
		Params.Instance->Frames.Remove(Params.Frame);
		Params.Process.FramePool.FreeItem(Params.Frame);
		Params.Process.FrameDataPool.FreeItem(*Params.Thread->Frames.GetLast());
		Params.Thread->Frames.vCount = Params.Thread->Frames.vCount - 1;
		OnFrameChanged(Params);
};

procedure OP_DefineLocal(const TOpDataPack Params)
{
			#ifdef DEBUG
			char vStr[100];
			sprintf(vStr,"local %s",*(Params.Instance->Script->Strings)[Params.Instruction->Parameter]);
			MessageBoxA(NULL,vStr,"Send!",MB_OK | MB_ICONWARNING);
			DebugLn(vStr);
			#endif
			DefineVariable(Params, Params.Instruction->Parameter, TScriptDataType(Params.Instruction->r1), vsLocal);
};

procedure OP_DefineGlobal(const TOpDataPack Params)
{
			#ifdef DEBUG
			char vStr[100];
			sprintf(vStr,"global %s",*(Params.Instance->Script->Strings)[Params.Instruction->Parameter]);
			MessageBoxA(NULL,vStr,"Send!",MB_OK | MB_ICONWARNING);
			DebugLn(vStr);
			#endif
			DefineVariable(Params, Params.Instruction->Parameter, TScriptDataType(Params.Instruction->r1), vsGlobal);

};

procedure OP_DefineArgument(const TOpDataPack Params)
{
	PScriptData Variable;

			Variable = DefineVariable(Params, Params.Instruction->Parameter, TScriptDataType(Params.Instruction->r1), vsLocal);
			Variable->IsArgument = TRUE;

				#ifdef DEBUG
				char vStr[100];
				sprintf(vStr,"argument %s = %d",*(Params.Instance->Script->Strings)[Params.Instruction->Parameter],
												*(PInt32(&Params.Instance->Frames.GetNextItem(Params.Frame)->Stack.Items[Params.Instance->Frames.GetNextItem(Params.Frame)->Stack.Length - 1 - Params.Instruction->r2]->Value)));
				MessageBoxA(NULL,vStr,"Send!",MB_OK | MB_ICONWARNING);
				DebugLn(vStr);
				#endif

				SetScriptDataValue(
					Params.Instance,
					Variable,
					Params.Instance->Frames.GetNextItem(Params.Frame)->Stack.Items[Params.Instance->Frames.GetNextItem(Params.Frame)->Stack.Length - 1 - Params.Instruction->r2]->Value,
					TScriptDataType(Params.Instruction->r1),
					ctVariable
				);
};

procedure PushValueToFrame(PInstance Instance, PFrame Frame, Dword AValue, TScriptDataType AType)
{
		InitScriptDataValue(
			Instance,
			*Frame->Stack.Items[Frame->Stack.Length],
			AValue,
			AType,
			ctStackItem
		);
		Frame->Stack.Length++;
};

procedure PopFrameValue(PInstance Instance, PFrame Frame, TScriptData Destination, TScriptDataContainerType DestinationContainerType)
{
		Frame->Stack.Length--;
		MoveScriptData(
			Instance,
			*Frame->Stack.Items[Frame->Stack.Length],
			Destination,
			DestinationContainerType
		);
};

procedure PopFrameValues(PInstance Instance, PFrame Frame, Integer Count)
{
	Integer vvi;

		for (vvi = 0; vvi <= Count - 1; vvi++)
		{
			Frame->Stack.Length--;
			DecrementScriptDataValueReferences(
				Instance,
				*Frame->Stack.Items[Frame->Stack.Length],
				ctStackItem
			);
		};
};

procedure OP_CleanStack(const TOpDataPack Params)
{
		#ifdef DEBUG
		char vStr[100];
		sprintf(vStr,"cleanstack %d",Params.Instruction->r1);
		MessageBoxA(NULL,vStr,"Send!",MB_OK | MB_ICONWARNING);
		DebugLn(vStr);
		#endif
		PopFrameValues(Params.Instance, Params.Frame, Params.Instruction->r1);
};

procedure OP_Literal(const TOpDataPack Params)
{
		#ifdef DEBUG
		char vStr[100];
		sprintf(vStr,"literal r%d = %d",Params.Instruction->r1,*PInt32(&Params.Instruction->Parameter));
		MessageBoxA(NULL,vStr,"Send!",MB_OK | MB_ICONWARNING);
		DebugLn(vStr);
		#endif

		SetScriptDataValue(
			Params.Instance,
			*Params.Registers[Params.Instruction->r1],
			Params.Instruction->Parameter,
			TScriptDataType(Params.Instruction->r2),
			ctRegister
		);
};

procedure OP_Move(const TOpDataPack Params)
{
			#ifdef DEBUG
			char vStr[100];
			sprintf(vStr,"move r%d = r%d",Params.Instruction->r2,Params.Instruction->r1);
			MessageBoxA(NULL,vStr,"Send!",MB_OK | MB_ICONWARNING);
			DebugLn(vStr);
			#endif
			CopyScriptData(Params.Instance, &Params.Instance->Registers[Params.Instruction->r2], &Params.Instance->Registers[Params.Instruction->r1], ctRegister);
};

Boolean OP_GetVar(TOpDataPack Params)
{
	Boolean Result;
	PScriptData Variable;

	Result = TRUE;

			#ifdef DEBUG
			char vStr[100];
			sprintf(vStr,"getvar %s -> r%d",*(Params.Instance->Script->Strings)[Params.Instruction->Parameter],Params.Instruction->r1);
			MessageBoxA(NULL,vStr,"Send!",MB_OK | MB_ICONWARNING);
			DebugLn(vStr);
			#endif
			// TODO: Add optional null pointer check
			Variable = GetVariable(Params.Instruction->Parameter, Params);

			if (Variable->InitializedType != stVoid)
			{
				SetScriptDataValue(
					Params.Instance,
					&Params.Instance->Registers[Params.Instruction->r1],
					Variable->Value,
					TScriptDataType(Params.Instruction->r2),
					ctRegister
				);
			}
			else
			{
				#ifdef DEBUG
				char vStr2[100];
				sprintf(vStr2,"Access to uninitialized variable "%s".",*(Params.Instance->Script->Strings)[Params.Instruction->Parameter]);
				MessageBoxA(NULL,vStr2,"Send!",MB_OK | MB_ICONWARNING);
				DebugLn(vStr2);
				#endif
				Params.ExitCode = scOverflowOrUndefined;
				Result = FALSE;
			};
	return Result;
};

procedure OP_GetCode(TOpDataPack Params)
{
			#ifdef DEBUG
			char vStr[100];
			sprintf(vStr,"getcode %s -> r%d",*(Instance->Script->Strings)[Params.Instruction->Parameter],Params.Instruction->r1);
			MessageBoxA(NULL,vStr,"Send!",MB_OK | MB_ICONWARNING);
			DebugLn(vStr);
			#endif
			// TODO: Add optional null pointer check
			SetScriptDataValue(
				Params.Instance,
				&Params.Instance->Registers[Params.Instruction->r1],
				*PtrUInt(Params.FastFunctions.GetValue(Params.Instruction->Parameter)->Address),
				TScriptDataType(Params.Instruction->r2),
				ctRegister
			);
};

procedure OP_GetArray(const TOpDataPack Params)
{
	PScriptData SourceVariable, DestinationRegister, IndexRegister;
	PJassArray Items;
	Dword Value = 0;

			#ifdef DEBUG
			char vStr[100];
			sprintf(vStr,"getarray %s -> r%d",*(Instance->Script->Strings)[Params.Instruction->Parameter],Params.Instruction->r1);
			MessageBoxA(NULL,vStr,"Send!",MB_OK | MB_ICONWARNING);
			DebugLn(vStr);
			#endif

		DestinationRegister = &Params.Instance->Registers[Params.Instruction->r1];
		SourceVariable = GetVariable(Params.Instruction->Parameter, Params);
		IndexRegister = &Params.Instance->Registers[Params.Instruction->r2];
		Items = PJassArray(SourceVariable->Value);

        // TODO: Dont forget to initialize array to prevent null pointer!
		// TODO: Add option to enable runtime range checks

		if (IndexRegister->Value < Items->List.GetLength())
		{
			Value = Items->List.FItems[IndexRegister->Value];
		};

		SetScriptDataValue(
			Params.Instance,
			DestinationRegister,
			Value,
			TScriptDataType(Params.Instruction->r3),
			ctRegister
		);
};

procedure OP_SetVar(const TOpDataPack Params)
{
	PScriptData Variable;
	#ifdef JASS_VM_VAR_CHANGE_EVENT_ENABLED
	Boolean Changed = FALSE;
	PObserver Observer;
	TGameVarChangedEvent Event;
	TVariableScope VarScope;
	#endif
		
			// TODO: Add optional null pointer check
			// TODO: should I handle null type?
			#ifdef DEBUG
			char vStr[100];
			sprintf(vStr,"setvar %s = %d",*(Instance->Script->Strings)[Params.Instruction->Parameter],
				*(PInt32(&&Params.Instance->Registers[Params.Instruction->r1].Value)));
			MessageBoxA(NULL,vStr,"Send!",MB_OK | MB_ICONWARNING);
			DebugLn(vStr);
			#endif
			
			#ifdef JASS_VM_VAR_CHANGE_EVENT_ENABLED
			if ((Params.Instance->VarChangedCallback != NULL)
				&& (VarChangeObservers.GetValue(Params.Instruction->Parameter, Observer)))
			{
				Variable = GetVariable(Params.Instruction->Parameter, Params, VarScope);
				Changed = &Params.Instance->Registers[Params.Instruction->r1].Value != Variable->Value;
				CopyScriptData(Params.Instance, &Params.Instance->Registers[Params.Instruction->r1], *(Variable), ctVariable);
				if ((VarScope == vsGlobal) && Changed)
				{
					#ifdef JASS_VM_VAR_CHANGE_EVENT_SLOW
					Params.Instance->VarChangedCallback(
						NULL,
						Variable->Value,
						Variable->HashKeyData.Key,
						Params.Instance->VarChangedCallbackParam
					);
					#else
					Event.Id = 0x80268;
					Event.NewValue = *(PSingle(&Variable->Value));
					Observer->Emit(Event);
					#endif
				};
			}
			else
			{
			#endif
				Variable = GetVariable(Params.Instruction->Parameter, Params);
				CopyScriptData(Params.Instance, &Params.Instance->Registers[Params.Instruction->r1], Variable, ctVariable);
			#ifdef JASS_VM_VAR_CHANGE_EVENT_ENABLED}
			};
			#endif
};

TScriptDataType ExtractArrayItemType(TScriptDataType AArrayType)
{
	return TScriptDataType(Dword(AArrayType) - 5);
};

procedure OP_SetArray(const TOpDataPack Params)
{
	PScriptData SourceRegister, DestinationVariable, IndexRegister;
	PJassArray Items;

	
		IndexRegister = &Params.Instance->Registers[Params.Instruction->r1];
		SourceRegister = &Params.Instance->Registers[Params.Instruction->r2];
		DestinationVariable = GetVariable(Params.Instruction->Parameter, Params);
		Items = PJassArray(DestinationVariable->Value);

		if (IndexRegister->Value < Items->List.GetLength())
		{
			// TODO: Dont forget to initialize array to prevent null pointer!
			// TODO: Add option to enable runtime range checks
			#ifdef DEBUG
			char vStr[100];
			sprintf(vStr,"setarray %s[%d] = %d",
				*(Params.Instance->Script->Strings)[Params.Instruction->Parameter],
				*(PInt32(&IndexRegister->Value)),
				*(PInt32(&SourceRegister->Value)));
			MessageBoxA(NULL,vStr,"Send!",MB_OK | MB_ICONWARNING);
			DebugLn(vStr);
			#endif

			ChangeValue(
				Params.Instance,
				Items->List.FItems[IndexRegister->Value],
				SourceRegister->Value,
				ExtractArrayItemType(DestinationVariable->_Type),
				ctVariable
			);
			// TODO: use callback when global variable changed
		};
};

procedure OP_Push(const TOpDataPack Params)
{
	
		// TODO: Dont forget to allocate all items of stack
		// TODO: add optional range check
			#ifdef DEBUG
			char vStr[100];
			sprintf(vStr,"push r%d",
				Params.Instruction->r1);
			MessageBoxA(NULL,vStr,"Send!",MB_OK | MB_ICONWARNING);
			DebugLn(vStr);
			#endif

			PushValueToFrame(Params.Instance, Params.Frame, Params.Instance->Registers[Params.Instruction->r1].Value, Params.Instance->Registers[Params.Instruction->r1]._Type);
};

procedure OP_Pop(const TOpDataPack Params)
{
	
		// TODO: Dont forget to allocate all items of stack
		// TODO: add optional range check
			#ifdef DEBUG
			char vStr[100];
			sprintf(vStr,"pop r%d",
				Params.Instruction->r1);
			MessageBoxA(NULL,vStr,"Send!",MB_OK | MB_ICONWARNING);
			DebugLn(vStr);
			#endif

		PopFrameValue(Params.Instance, Params.Frame, Params.Instance->Registers[Params.Instruction->r1], ctRegister);
};

Dword __stdcall ASM_CallNative(Pointer pNative, Pointer Arguments, Dword Count)
{
__asm
{
	push esi
	push edi

	mov ecx, Count

	mov eax, ecx
	shl eax, 2
	sub esp, eax

	mov esi, Arguments
	mov edi, esp
	cld
	repe movsd

	mov esi, eax

	call [pNative]

	add esp, esi

	pop edi
	pop esi
};
};

Boolean OP_NativeCall(TOpDataPack Params)
{
	// With a return address pushed equals to a maximum stack capacity.
const Dword MAX_ARGUMENTS = 31;

	Boolean Result;
	PNativeFunc NativeFunction;
	TNativeData NativeExtraData(*NativeFunction);
	PScriptData CurrentArgument;
	Dword ConvertedValue;
	Integer vvi;
	Dword CallResult;
	Dword Arguments[MAX_ARGUMENTS];
	TRCString Strings[MAX_ARGUMENTS];
	Integer StringsCount = 0;

	Result = TRUE;

	
		NativeFunction = Params.FastNatives.GetValue(Params.Instruction->Parameter);
		NativeExtraData = Params.FastNativesExtra.GetValue(Params.Instruction->Parameter);

			for (vvi = 0; vvi <= sizeof(*NativeExtraData.ArgumentsType)/sizeof(NativeExtraData.ArgumentsType[0]) - 1; vvi++)
			{
				CurrentArgument = Params.Frame->Stack.Items[
					Params.Frame->Stack.Length - sizeof(*NativeExtraData.ArgumentsType)/sizeof(NativeExtraData.ArgumentsType[0]) + vvi
				];

				switch (NativeExtraData.ArgumentsType[vvi])
				{
					case stCode:
					{
						if (CurrentArgument->Value != 0)
						{
							ConvertedValue = Params.Instance->CodeTable.GetHandle(
								PInstruction(CurrentArgument->Value)
							);
							#ifdef NATIVE_CALLBACKS_SUPPORT
							ConvertedValue = pTransformJassCodeHandle(ConvertedValue);
							#endif
						}
						else
						{
							ConvertedValue = 0;
						};
					};

					case stReal:
					{
						ConvertedValue = Dword(&CurrentArgument->Value);
					};

					case stString:
					{
						Strings[StringsCount].TRCString::TRCString();
						Strings[StringsCount].SetContent(Params.Instance->StringTable.GetItem(CurrentArgument->Value).GetContent());

						ConvertedValue = Dword(&Strings[StringsCount]);

						StringsCount++;
					};

					default:
					{
						ConvertedValue = CurrentArgument->Value;
					};
				};

				Arguments[vvi] = ConvertedValue;
			};

			#ifdef DEBUG
			char vStr[100];
			sprintf(vStr,"call native %s",
				NativeFunction->HashKeyData.Key);
			MessageBoxA(NULL,vStr,"Send!",MB_OK | MB_ICONWARNING);
			DebugLn(vStr);
			#endif
			//OnNativeCall(Instance, Instruction->Parameter);

			CallResult = ASM_CallNative(
				NativeFunction->Address,
				&Arguments[0],
				sizeof(*NativeExtraData.ArgumentsType)/sizeof(NativeExtraData.ArgumentsType[0])
			);

			for (vvi = 0; vvi <= StringsCount - 1; vvi++)
			{
				Strings[vvi].~TRCString();
			};

			// Dont increment references - native function already did it
			DecrementScriptDataValueReferences(
				Params.Instance,
				Params.Instance->Registers[0],
				ctRegister
			);
				Params.Instance->Registers[0].Value = CallResult;
				Params.Instance->Registers[0]._Type = NativeFunction->rettype;

			PopFrameValues(Params.Instance, Params.Frame, sizeof(*NativeExtraData.ArgumentsType)/sizeof(NativeExtraData.ArgumentsType[0]));

		if (Params.Instance->Sleeping)
		{
			Params.ExitCode = scSleeping;
			Result = FALSE;
		}
		else if (Params.Instance->Syncing)
		{
			Params.ExitCode = scSyncing;
			Result = FALSE;
		};
	return Result;
};

procedure OP_Call(TOpDataPack Params)
{
	
			#ifdef DEBUG
			char vStr[100];
			sprintf(vStr,"call %s",
				Params.FastFunctions[Params.Instruction->Parameter]->HashKeyData.Key);
			MessageBoxA(NULL,vStr,"Send!",MB_OK | MB_ICONWARNING);
			DebugLnEnter(vStr);
			#endif
		//OnCall(Instance, Instruction->Parameter);

		PushValueToFrame(
			Params.Instance,
			Params.Frame,
			Dword(Dword(Params.Instruction + 1) - Dword(Params.Code)) / sizeof(Dword),
			stRetAddr
		);

		StartFrame(Params);

		Params.Instance->CurrentOpcode = (PInstruction)Params.FastFunctions.GetValue(Params.Instruction->Parameter)->Address;
};

Dword SingleAsDword(Single Value)
{
	Dword Result;
	*(PSingle(&Result)) = Value;
	return Result;
};

procedure OP_I2R(TOpDataPack Params)
{
			#ifdef DEBUG
			char vStr[100];
			sprintf(vStr,"i2r r%d",
				Params.Instruction->r1);
			MessageBoxA(NULL,vStr,"Send!",MB_OK | MB_ICONWARNING);
			DebugLn(vStr);
			#endif

			SetScriptDataValue(
				Params.Instance,
				&Params.Instance->Registers[Params.Instruction->r1],
				SingleAsDword((Single)Params.Instance->Registers[Params.Instruction->r1].Value),
				stReal,
				ctRegister
			);
};

procedure OP_And(TOpDataPack Params)
{
			#ifdef DEBUG
			char vStr[100];
			sprintf(vStr,"and r%d, r%d -> r%d",
				Params.Instruction->r2,
				Params.Instruction->r3,
				Params.Instruction->r1);
			MessageBoxA(NULL,vStr,"Send!",MB_OK | MB_ICONWARNING);
			DebugLn(vStr);
			#endif

			SetScriptDataValue(
				Params.Instance,
				&Params.Instance->Registers[Params.Instruction->r1],
				Dword(*(PLongBool(&Params.Instance->Registers[Params.Instruction->r2].Value))
					&& *(PLongBool(&Params.Instance->Registers[Params.Instruction->r3].Value))),
				stBoolean,
				ctRegister
			);
};

procedure OP_Or(TOpDataPack Params)
{
			#ifdef DEBUG
			char vStr[100];
			sprintf(vStr,"or r%d, r%d -> r%d",
				Params.Instruction->r2,
				Params.Instruction->r3,
				Params.Instruction->r1);
			MessageBoxA(NULL,vStr,"Send!",MB_OK | MB_ICONWARNING);
			DebugLn(vStr);
			#endif

			SetScriptDataValue(
				Params.Instance,
				&Params.Instance->Registers[Params.Instruction->r1],
				Dword(*(PLongBool(&Params.Instance->Registers[Params.Instruction->r2].Value))
					|| *(PLongBool(&Params.Instance->Registers[Params.Instruction->r3].Value))),
				stBoolean,
				ctRegister
			);
};

procedure OP_Equal(TOpDataPack Params)
{
			#ifdef DEBUG
			char vStr[100];
			sprintf(vStr,"equal r%d, r%d -> r%d",
				Params.Instruction->r2,
				Params.Instruction->r3,
				Params.Instruction->r1);
			MessageBoxA(NULL,vStr,"Send!",MB_OK | MB_ICONWARNING);
			DebugLn(vStr);
			#endif

			switch (Params.Instance->Registers[Params.Instruction->r2]._Type)
			{
				case stReal:
				{
					SetScriptDataValue(
						Params.Instance,
						&Params.Instance->Registers[Params.Instruction->r1],
						Dword(
							(*(PJassFloat(&Params.Instance->Registers[Params.Instruction->r2].Value))).IsApproximatelyEqualTo(
									*(PJassFloat(&Params.Instance->Registers[Params.Instruction->r3].Value)),0.001)
						),
						stBoolean,
						ctRegister
					);
				};
				break;

				case stBoolean:
				{
					SetScriptDataValue(
						Params.Instance,
						&Params.Instance->Registers[Params.Instruction->r1],
						Dword(*PLongBool(&Params.Instance->Registers[Params.Instruction->r2].Value)
							== *PLongBool(&Params.Instance->Registers[Params.Instruction->r3].Value)),
						stBoolean,
						ctRegister
					);
				};
				break;

				default:
				{
					SetScriptDataValue(
						Params.Instance,
						&Params.Instance->Registers[Params.Instruction->r1],
						Dword(Params.Instance->Registers[Params.Instruction->r2].Value
							== Params.Instance->Registers[Params.Instruction->r3].Value),
						stBoolean,
						ctRegister
					);
				};
				break;
			};
};

procedure OP_NotEqual(TOpDataPack Params)
{
			#ifdef DEBUG
			char vStr[100];
			sprintf(vStr,"not_equal r%d, r%d -> r%d",
				Params.Instruction->r2,
				Params.Instruction->r3,
				Params.Instruction->r1);
			MessageBoxA(NULL,vStr,"Send!",MB_OK | MB_ICONWARNING);
			DebugLn(vStr);
			#endif

			switch (Params.Instance->Registers[Params.Instruction->r2]._Type)
			{
				case stBoolean:
				{
					SetScriptDataValue(
						Params.Instance,
						&Params.Instance->Registers[Params.Instruction->r1],
						Dword(*(PLongBool(&Params.Instance->Registers[Params.Instruction->r2].Value))
							!= *(PLongBool(&Params.Instance->Registers[Params.Instruction->r3].Value))),
						stBoolean,
						ctRegister
					);
				};
				break;

				default:
				{
					SetScriptDataValue(
						Params.Instance,
						&Params.Instance->Registers[Params.Instruction->r1],
						Dword(Params.Instance->Registers[Params.Instruction->r2].Value
							!= Params.Instance->Registers[Params.Instruction->r3].Value),
						stBoolean,
						ctRegister
					);
				};
				break;
			};
};

procedure OP_LessEqual(TOpDataPack Params)
{
			#ifdef DEBUG
			char vStr[100];
			sprintf(vStr,"less_equal r%d, r%d -> r%d",
				Params.Instruction->r2,
				Params.Instruction->r3,
				Params.Instruction->r1);
			MessageBoxA(NULL,vStr,"Send!",MB_OK | MB_ICONWARNING);
			DebugLn(vStr);
			#endif

			switch (Params.Instance->Registers[Params.Instruction->r2]._Type)
			{
				case stInteger:
				{
					SetScriptDataValue(
						Params.Instance,
						&Params.Instance->Registers[Params.Instruction->r1],
						Dword(*(PInt32(&Params.Instance->Registers[Params.Instruction->r2].Value))
							<= *(PInt32(&Params.Instance->Registers[Params.Instruction->r3].Value))),
						stBoolean,
						ctRegister
					);
				};
				break;

				case stReal:
				{
					SetScriptDataValue(
						Params.Instance,
						&Params.Instance->Registers[Params.Instruction->r1],
						Dword(*(PJassFloat(&Params.Instance->Registers[Params.Instruction->r2].Value))
							<= *(PJassFloat(&Params.Instance->Registers[Params.Instruction->r3].Value))),
						stBoolean,
						ctRegister
					);
				};
				break;
			};
};

procedure OP_GreaterEqual(TOpDataPack Params)
{
			#ifdef DEBUG
			char vStr[100];
			sprintf(vStr,"greater_equal r%d, r%d -> r%d",
				Params.Instruction->r2,
				Params.Instruction->r3,
				Params.Instruction->r1);
			MessageBoxA(NULL,vStr,"Send!",MB_OK | MB_ICONWARNING);
			DebugLn(vStr);
			#endif

			switch (Params.Instance->Registers[Params.Instruction->r2]._Type)
			{
				case stInteger:
				{
					SetScriptDataValue(
						Params.Instance,
						&Params.Instance->Registers[Params.Instruction->r1],
						Dword(*(PInt32(&Params.Instance->Registers[Params.Instruction->r2].Value))
							>= *(PInt32(&Params.Instance->Registers[Params.Instruction->r3].Value))),
						stBoolean,
						ctRegister
					);
				};
				break;

				case stReal:
				{
					SetScriptDataValue(
						Params.Instance,
						&Params.Instance->Registers[Params.Instruction->r1],
						Dword(*(PJassFloat(&Params.Instance->Registers[Params.Instruction->r2].Value))
							>= *(PJassFloat(&Params.Instance->Registers[Params.Instruction->r3].Value))),
						stBoolean,
						ctRegister
					);
				};
				break;
			};
};

procedure OP_Less(TOpDataPack Params)
{
			#ifdef DEBUG
			char vStr[100];
			sprintf(vStr,"less r%d, r%d -> r%d",
				Params.Instruction->r2,
				Params.Instruction->r3,
				Params.Instruction->r1);
			MessageBoxA(NULL,vStr,"Send!",MB_OK | MB_ICONWARNING);
			DebugLn(vStr);
			#endif

			switch (Params.Instance->Registers[Params.Instruction->r2]._Type)
			{
				case stInteger:
				{
					SetScriptDataValue(
						Params.Instance,
						&Params.Instance->Registers[Params.Instruction->r1],
						Dword(*(PInt32(&Params.Instance->Registers[Params.Instruction->r2].Value))
							< *(PInt32(&Params.Instance->Registers[Params.Instruction->r3].Value))),
						stBoolean,
						ctRegister
					);
				};
				break;

				case stReal:
				{
					SetScriptDataValue(
						Params.Instance,
						&Params.Instance->Registers[Params.Instruction->r1],
						Dword(*(PJassFloat(&Params.Instance->Registers[Params.Instruction->r2].Value))
							< *(PJassFloat(&Params.Instance->Registers[Params.Instruction->r3].Value))),
						stBoolean,
						ctRegister
					);
				};
				break;
			};
};

procedure OP_Greater(TOpDataPack Params)
{
			#ifdef DEBUG
			char vStr[100];
			sprintf(vStr,"greater r%d, r%d -> r%d",
				Params.Instruction->r2,
				Params.Instruction->r3,
				Params.Instruction->r1);
			MessageBoxA(NULL,vStr,"Send!",MB_OK | MB_ICONWARNING);
			DebugLn(vStr);
			#endif

			switch (Params.Instance->Registers[Params.Instruction->r2]._Type)
			{
				case stInteger:
				{
					SetScriptDataValue(
						Params.Instance,
						&Params.Instance->Registers[Params.Instruction->r1],
						Dword(*(PInt32(&Params.Instance->Registers[Params.Instruction->r2].Value))
							> *(PInt32(&Params.Instance->Registers[Params.Instruction->r3].Value))),
						stBoolean,
						ctRegister
					);
				};
				break;

				case stReal:
				{
					SetScriptDataValue(
						Params.Instance,
						&Params.Instance->Registers[Params.Instruction->r1],
						Dword(*(PJassFloat(&Params.Instance->Registers[Params.Instruction->r2].Value))
							> *(PJassFloat(&Params.Instance->Registers[Params.Instruction->r3].Value))),
						stBoolean,
						ctRegister
					);
				};
				break;
			};
};

String ConcatenateStrings(PChar a, PChar b)
{
	String Result;
const String NULL_STRING = "";
const String EMPTY_STRING = "\0";

	SizeInt LengthA, LengthB, TotalLength;

	if ((a == NULL) && (b == NULL))
	{
		Result = NULL_STRING;
	}
	else
	{
		LengthA = strlen(a);
		LengthB = strlen(b);
		TotalLength = LengthA + LengthB;

		if (TotalLength > 0)
		{
			//memset(Result, 0, TotalLength);
			//if (LengthA > 0) memcpy(&Result[1],a,LengthA);
			//if (LengthB > 0) memcpy(&Result[1 + LengthA],b,LengthB);
			sprintf(Result,"%s%s\0",a,b);
		}
		else
		{
			Result = EMPTY_STRING;
		};
	};
	return Result;
};

procedure OP_Add(TOpDataPack Params)
{
	String ResultString;
	Dword StringHandle;

			#ifdef DEBUG
			char vStr[100];
			sprintf(vStr,"add r%d, r%d -> r%d",
				Params.Instruction->r2,
				Params.Instruction->r3,
				Params.Instruction->r1);
			MessageBoxA(NULL,vStr,"Send!",MB_OK | MB_ICONWARNING);
			DebugLn(vStr);
			#endif

			switch (Params.Instance->Registers[Params.Instruction->r2]._Type)
			{
				case stInteger:
				{
					SetScriptDataValue(
						Params.Instance,
						&Params.Instance->Registers[Params.Instruction->r1],
						Dword(*(PInt32(&Params.Instance->Registers[Params.Instruction->r2].Value))
							+ *(PInt32(&Params.Instance->Registers[Params.Instruction->r3].Value))),
						stInteger,
						ctRegister
					);
				};
				break;

				case stReal:
				{
					SetScriptDataValue(
						Params.Instance,
						&Params.Instance->Registers[Params.Instruction->r1],
						SingleAsDword(
							Single(
								Single(Params.Instance->Registers[Params.Instruction->r2].Value)
								+ Single(Params.Instance->Registers[Params.Instruction->r3].Value)
							)
						),
						stReal,
						ctRegister
					);
				};
				break;

				case stString:
				{
					ResultString = ConcatenateStrings(
						GetStringContent(Params.Instance, Params.Instance->Registers[Params.Instruction->r2].Value),
						GetStringContent(Params.Instance, Params.Instance->Registers[Params.Instruction->r3].Value)
					);

					StringHandle = GetStringHandle(
						Params.Instance,
						PChar(ResultString)
					);

					DecrementScriptDataValueReferences(
						Params.Instance,
						Params.Instance->Registers[Params.Instruction->r1],
						ctRegister
					);
					// GetStringHandle function already incremented reference counter
						Params.Instance->Registers[Params.Instruction->r1].Value = StringHandle;
						Params.Instance->Registers[Params.Instruction->r1]._Type = stString;
				};
				break;
			};
};

procedure OP_Sub(TOpDataPack Params)
{
			#ifdef DEBUG
			char vStr[100];
			sprintf(vStr,"sub r%d, r%d -> r%d",
				Params.Instruction->r2,
				Params.Instruction->r3,
				Params.Instruction->r1);
			MessageBoxA(NULL,vStr,"Send!",MB_OK | MB_ICONWARNING);
			DebugLn(vStr);
			#endif

			switch (Params.Instance->Registers[Params.Instruction->r2]._Type)
			{
				case stInteger:
				{
					SetScriptDataValue(
						Params.Instance,
						&Params.Instance->Registers[Params.Instruction->r1],
						Dword(*(PInt32(&Params.Instance->Registers[Params.Instruction->r2].Value))
							- *(PInt32(&Params.Instance->Registers[Params.Instruction->r3].Value))),
						stInteger,
						ctRegister
					);
				};
				break;

				case stReal:
				{
					SetScriptDataValue(
						Params.Instance,
						&Params.Instance->Registers[Params.Instruction->r1],
						SingleAsDword(
							Single(
								Single(Params.Instance->Registers[Params.Instruction->r2].Value)
								- Single(Params.Instance->Registers[Params.Instruction->r3].Value)
							)
						),
						stReal,
						ctRegister
					);
				};
				break;
			};
};

procedure OP_Mul(TOpDataPack Params)
{
			#ifdef DEBUG
			char vStr[100];
			sprintf(vStr,"mul r%d, r%d -> r%d",
				Params.Instruction->r2,
				Params.Instruction->r3,
				Params.Instruction->r1);
			MessageBoxA(NULL,vStr,"Send!",MB_OK | MB_ICONWARNING);
			DebugLn(vStr);
			#endif

			switch (Params.Instance->Registers[Params.Instruction->r2]._Type)
			{
				case stInteger:
				{
					SetScriptDataValue(
						Params.Instance,
						&Params.Instance->Registers[Params.Instruction->r1],
						Dword(*(PInt32(&Params.Instance->Registers[Params.Instruction->r2].Value))
							* *(PInt32(&Params.Instance->Registers[Params.Instruction->r3].Value))),
						stInteger,
						ctRegister
					);
				};
				break;

				case stReal:
				{
					SetScriptDataValue(
						Params.Instance,
						&Params.Instance->Registers[Params.Instruction->r1],
						SingleAsDword(
							Single(
								Single(Params.Instance->Registers[Params.Instruction->r2].Value)
								* Single(Params.Instance->Registers[Params.Instruction->r3].Value)
							)
						),
						stReal,
						ctRegister
					);
				};
				break;
			};
};

Boolean OP_Div(TOpDataPack Params) 
{
	Boolean Result;
	Result = TRUE;

			#ifdef DEBUG
			char vStr[100];
			sprintf(vStr,"div r%d, r%d -> r%d",
				Params.Instruction->r2,
				Params.Instruction->r3,
				Params.Instruction->r1);
			MessageBoxA(NULL,vStr,"Send!",MB_OK | MB_ICONWARNING);
			DebugLn(vStr);
			#endif

			if (Params.Instance->Registers[Params.Instruction->r3].Value == 0)
			{
				Params.ExitCode = scZeroDivision;
				return FALSE;
			};

			switch (Params.Instance->Registers[Params.Instruction->r2]._Type)
			{
				case stInteger:
				{
					SetScriptDataValue(
						Params.Instance,
						&Params.Instance->Registers[Params.Instruction->r1],
						Dword(*(PInt32(&Params.Instance->Registers[Params.Instruction->r2].Value))
							/ *(PInt32(&Params.Instance->Registers[Params.Instruction->r3].Value))),
						stInteger,
						ctRegister
					);
				};
				break;

				case stReal:
				{
					SetScriptDataValue(
						Params.Instance,
						&Params.Instance->Registers[Params.Instruction->r1],
						SingleAsDword(
							Single(
								Single(Params.Instance->Registers[Params.Instruction->r2].Value)
								/ Single(Params.Instance->Registers[Params.Instruction->r3].Value)
							)
						),
						stReal,
						ctRegister
					);
				};
				break;
			};
	return Result;
};

procedure OP_Modulo(TOpDataPack Params)
{
			#ifdef DEBUG
			char vStr[100];
			sprintf(vStr,"mod r%d, r%d -> r%d",
				Params.Instruction->r2,
				Params.Instruction->r3,
				Params.Instruction->r1);
			MessageBoxA(NULL,vStr,"Send!",MB_OK | MB_ICONWARNING);
			DebugLn(vStr);
			#endif

			switch (Params.Instance->Registers[Params.Instruction->r2]._Type)
			{
				case stInteger:
				{
					if (Params.Instance->Registers[Params.Instruction->r3].Value != 0)
					{
						SetScriptDataValue(
							Params.Instance,
							&Params.Instance->Registers[Params.Instruction->r1],
							Dword(*(PInt32(&Params.Instance->Registers[Params.Instruction->r2].Value))
								% *(PInt32(&Params.Instance->Registers[Params.Instruction->r3].Value))),
							stInteger,
							ctRegister
						);
					}
					else
					{
						SetScriptDataValue(
							Params.Instance,
							&Params.Instance->Registers[Params.Instruction->r1],
							0,
							stInteger,
							ctRegister
						);
					};
				};
				break;
			};
};

procedure OP_Negate(TOpDataPack Params)
{
			#ifdef DEBUG
			char vStr[100];
			sprintf(vStr,"negate r%d",
				Params.Instruction->r1);
			MessageBoxA(NULL,vStr,"Send!",MB_OK | MB_ICONWARNING);
			DebugLn(vStr);
			#endif

				switch (Params.Instance->Registers[Params.Instruction->r1]._Type)
				{
					case stInteger:
					{
						*(PInt32(&Params.Instance->Registers[Params.Instruction->r1].Value)) = -*(PInt32(&Params.Instance->Registers[Params.Instruction->r1].Value));
					};
					break;

					case stReal:
					{
						*(PJassFloat(&Params.Instance->Registers[Params.Instruction->r1].Value)) = -*(PJassFloat(&Params.Instance->Registers[Params.Instruction->r1].Value));
					};
					break;
				};
};

procedure OP_Not(TOpDataPack Params)
{
			#ifdef DEBUG
			char vStr[100];
			sprintf(vStr,"not r%d",
				Params.Instruction->r1);
			MessageBoxA(NULL,vStr,"Send!",MB_OK | MB_ICONWARNING);
			DebugLn(vStr);
			#endif

			SetScriptDataValue(
				Params.Instance,
				&Params.Instance->Registers[Params.Instruction->r1],
				Dword(! *(PLongBool(&Params.Instance->Registers[Params.Instruction->r1].Value))),
				stBoolean,
				ctRegister
			);
};

Boolean OP_Return(TOpDataPack Params)
{
	Boolean Result;
	Boolean FinalFrame, ShouldStop;

	#ifdef DEBUG
	DebugLn("return");
    #endif

	
		//OnReturn(Instance);

		FinalFrame = Params.Frame->IsFirstFrame;
		EndFrame(Params);

		ShouldStop = FinalFrame || (Params.Frame == NULL);

		if (! ShouldStop)
		{
				#ifdef DEBUG
					if (Params.Frame->Stack.Items[Params.Frame->Stack.Length-1]->_Type != stRetAddr)
					{
						char vStr[100];
						sprintf(vStr,"Excepted return address but got type(%d).",
							Params.Frame->Stack.Items[Params.Frame->Stack.Length-1]->_Type);
						MessageBoxA(NULL,vStr,"Send!",MB_OK | MB_ICONWARNING);
					};
				#endif
				Params.Instance->CurrentOpcode = (PInstruction)(Dword(Params.Code) + (Params.Frame->Stack.Items[Params.Frame->Stack.Length-1]->Value * sizeof(Dword)));
				Params.Frame->Stack.Length--;
		};

		Result = ! ShouldStop;

	#ifdef DEBUG
		DebugLn("Exit");
    #endif
	return Result;
};

procedure OP_JumpIfTrue(TOpDataPack Params)
{
		#ifdef DEBUG
			char vStr[100];
			sprintf(vStr,"jump_if r%d to %d",
				Params.Instruction->r1,
				*(PInt32(&Params.Instruction->Parameter)));
			MessageBoxA(NULL,vStr,"Send!",MB_OK | MB_ICONWARNING);
			DebugLn(vStr);
		#endif
		if (Params.Instance->Registers[Params.Instruction->r1].Value != 0)
		{
			// TODO: Add optional range check
			Params.Instance->CurrentOpcode = (PInstruction)Params.Instance->Labels->GetItem(Params.Instruction->Parameter);
		};
};

procedure OP_JumpIfFalse(TOpDataPack Params)
{
		#ifdef DEBUG
			char vStr[100];
			sprintf(vStr,"jump_if_not r%d to %d",
				Params.Instruction->r1,
				*(PInt32(&Params.Instruction->Parameter)));
			MessageBoxA(NULL,vStr,"Send!",MB_OK | MB_ICONWARNING);
			DebugLn(vStr);
		#endif
		if (&Params.Instance->Registers[Params.Instruction->r1].Value == 0)
		{
			// TODO: Add optional range check
			Params.Instance->CurrentOpcode = (PInstruction)Params.Instance->Labels->GetItem(Params.Instruction->Parameter);
		};
};

procedure OP_Jump(TOpDataPack Params)
{
		#ifdef DEBUG
			char vStr[100];
			sprintf(vStr,"jump to %d",
				*(PInt32(&Params.Instruction->Parameter)));
			MessageBoxA(NULL,vStr,"Send!",MB_OK | MB_ICONWARNING);
			DebugLn(vStr);
		#endif
		// TODO: Add optional range check
		Params.Instance->CurrentOpcode = Params.Instance->Labels->GetItem(Params.Instruction->Parameter);
};

procedure InitializeParameters(TOpDataPack &Params, PInstance AInstance)
{
	TThreadData* MyThread;

	MyThread = new TThreadData((TProcessData*)JassEnvironment.ThreadTable->GetData(AInstance->Handle) );

	
		Params.Instance = AInstance;
		// Instruction: initialized at start of each iteration
		Params.Frame = NULL;
		Params.Locals = NULL;
		Params.Globals = AInstance->Globals;
		Params.ScriptStrings = (PPHashedString)&AInstance->Script->Strings->FItems;
		Params.Code = AInstance->Script->Code;
		Params.Registers = (TInstance::PRegisters)&AInstance->Registers[0];
		Params.Process = MyThread->FProcess;
		Params.Thread = MyThread;
		Params.FastGlobals = MyThread->FProcess.Globals;
		// FastLocals: initialized on frame creation
		Params.FastFunctions = MyThread->FProcess.Functions;
		Params.FastNatives = MyThread->FProcess.Natives;
		Params.FastNativesExtra = MyThread->FProcess.NativesData;
		Params.VarChangeObservers = MyThread->FProcess.VarChangeObservers;
};

procedure FinalizeParameters(TOpDataPack Params)
{
		Params.Process.Globals = Params.FastGlobals;
		Params.Process.Functions = Params.FastFunctions;
		Params.Process.Natives = Params.FastNatives;
		Params.Process.NativesData = Params.FastNativesExtra;
};

procedure BeginExecution(const TOpDataPack Params)
{
		GetJassThreadLocal()->RunningInstancesCount++;

		GetJassThreadLocal()->RunningInstances.SetLength(GetJassThreadLocal()->RunningInstancesCount);
		GetJassThreadLocal()->RunningInstances.SetItem(GetJassThreadLocal()->RunningInstancesCount-1,Params.Instance);
};

procedure EndExecution(const TOpDataPack Params)
{
		GetJassThreadLocal()->RunningInstancesCount--;
};

procedure StartExecution(TOpDataPack Params)
{
	#ifndef DISABLE_JASS_OPERATION_LIMIT
	Dword ExecutedInstructions = 0;
	Dword OperationsLimit;
	#endif

	BeginExecution(Params);

		Params.Instance->Sleeping = FALSE;
		Params.Instance->Syncing = FALSE;

	#ifndef DISABLE_JASS_OPERATION_LIMIT
	OperationsLimit = Params.Instance->OperationsLimit;
	#endif
	Params.ExitCode = scExecutionCompleted;

	while (TRUE)
	{
		#ifndef DISABLE_JASS_OPERATION_LIMIT
		if (ExecutedInstructions >= OperationsLimit)
		{
			Params.ExitCode = scOpLimitReached;
			break;
		};
		#endif

		Params.Instruction = Params.Instance->CurrentOpcode;
		Params.Instance->CurrentOpcode++;

		switch (TOperationCode(Params.Instruction->id))
		{
			case opDebug: OP_Jump(Params); break;
			case opLocal: OP_DefineLocal(Params); break;
			case opGlobal:
			case opConstant:
				OP_DefineGlobal(Params); break;
			case opArgument: OP_DefineArgument(Params); break;
			case opCleanStack: OP_CleanStack(Params); break;
			case opLiteral: OP_Literal(Params); break;
			case opMove: OP_Move(Params); break;
			case opGetVar:
				if (!OP_GetVar(Params))
					goto m1;
				break;
			case opGetCode: OP_GetCode(Params); break;
			case opGetArray: OP_GetArray(Params); break;
			case opSetVar: OP_SetVar(Params); break;
			case opSetArray: OP_SetArray(Params); break;
			case opPush: OP_Push(Params); break;
			case opPop: OP_Pop(Params); break;
			case opNativeCall:
				if (!OP_NativeCall(Params))
					goto m1;
				break;
			case opCall: OP_Call(Params); break;
			case opI2R: OP_I2R(Params); break;
			case opAnd: OP_And(Params); break;
			case opOr: OP_Or(Params); break;
			case opEqual: OP_Equal(Params); break;
			case opNotEqual: OP_NotEqual(Params); break;
			case opLesserEqual: OP_LessEqual(Params); break;
			case opGreaterEqual: OP_GreaterEqual(Params); break;
			case opLesser: OP_Less(Params); break;
			case opGreater: OP_Greater(Params); break;
			case opAdd: OP_Add(Params); break;
			case opSub: OP_Sub(Params); break;
			case opMul: OP_Mul(Params); break;
			case opDiv:
				if (!OP_Div(Params))
					goto m1;
				break;
			case opModulo: OP_Modulo(Params); break;
			case opNegate: OP_Negate(Params); break;
			case opNot: OP_Not(Params); break;
			case opReturn:
				if (!OP_Return(Params))
					goto m1;
				break;
			case opJumpIfTrue: OP_JumpIfTrue(Params); break;
			case opJumpIfFalse: OP_JumpIfFalse(Params); break;
			case opJump: OP_Jump(Params); break;
		};

		#ifndef DISABLE_JASS_OPERATION_LIMIT
		ExecutedInstructions++;
		#endif
	};
m1:
	EndExecution(Params);
};

TScriptExitCode ResumeExecution(PInstance Instance, PDword ScriptResult)
{
	TScriptExitCode Result;
	TOpDataPack Params;

	InitializeParameters(Params, Instance);
	OnFrameChanged(Params);
	StartExecution(Params);
	FinalizeParameters(Params);
	Result = Params.ExitCode;
	if (ScriptResult != NULL)
	{
		*(ScriptResult) = Instance->Registers[0].Value;
	};
	return Result;
};

TScriptExitCode ExecuteCode(PInstance pInstance, PInstruction pCode, PDword ScriptResult)
{
	TScriptExitCode Result;
	TOpDataPack Params;

	pInstance->CurrentOpcode = pCode;
	pInstance->OperationsLimit = DEFAULT_JASS_OPERATION_LIMIT;

	InitializeParameters(Params, pInstance);
	StartFrame(Params);
	StartExecution(Params);
	Result = Params.ExitCode;
	FinalizeParameters(Params);

	if (ScriptResult != NULL)
	{
		(*ScriptResult) = pInstance->Registers[0].Value;
	};
	return Result;
};

procedure OnInstanceInit(TInstanceHandle hInstance)
{
	Integer vvi;
	PInstance Instance;
	PInitFuncAddr InitFunc;
	TScriptExitCode ExitCode;

	#ifdef DEBUG
			char vStr[100];
			sprintf(vStr,"Initializing instance %d.",
				hInstance);
			MessageBoxA(NULL,vStr,"Send!",MB_OK | MB_ICONWARNING);
	DebugLnEnter(vStr);
	#endif
	Instance = GetJassThreadLocal()->Instances.GetItem(hInstance);

	for (vvi = 0; vvi <= Instance->InitFuncs->GetLength() - 1; vvi++)
	{
		if (vvi == 0)
		{
			InitFunc = Instance->InitFuncs->GetFirst();
		}
		else
		{
			InitFunc = Instance->InitFuncs->GetNextItem(InitFunc);
		}
		ExitCode = ExecuteCode(Instance, (PInstruction)InitFunc->Address,NULL);
		CleanupInstanceFrames(Instance);
		#ifdef DEBUG
		char vStr2[100];
			sprintf(vStr2,"ExitCode: %d;",
				ExitCode);
			MessageBoxA(NULL,vStr2,"Send!",MB_OK | MB_ICONWARNING);
		DebugLn(vStr2);
		#endif
	};
	#ifdef DEBUG
	DebugLnExit();
	#endif
};

TScriptExitCode OnInstanceExecuteFunctionByName(TInstanceHandle hInstance,
PChar Name, PDword ScriptResult)
{
	TScriptExitCode Result;
	PInstance Instance;
	PFuncAddr JassFunction;

	#ifdef DEBUG
			char vStr[100];
			sprintf(vStr,"Executing function ""%s"".",
				Name);
			MessageBoxA(NULL,vStr,"Send!",MB_OK | MB_ICONWARNING);
	DebugLnEnter(vStr);
	#endif
	Instance = GetJassThreadLocal()->Instances.GetItem(hInstance);

	if (Instance->NameToFuncAddr->Find(Name, JassFunction))
	{
		Result = ExecuteCode(Instance, (PInstruction)JassFunction->Address, ScriptResult);
		#ifdef DEBUG
			char vStr2[100];
			sprintf(vStr2,"ExitCode: %d",
				Integer(Result));
			MessageBoxA(NULL,vStr2,"Send!",MB_OK | MB_ICONWARNING);
		DebugLnExit(vStr2);
		#endif
	}
	else
	{
		#ifdef DEBUG
			char vStr3[100];
			sprintf(vStr3,"Function ""%s"" not found.",
				Name);
			MessageBoxA(NULL,vStr3,"Send!",MB_OK | MB_ICONWARNING);
		DebugLnExit(vStr3);
		#endif
		Result = scFunctionNotFound;
	};
	return Result;
};

String GetCodeName(PInstance Instance, PInstruction Code)
{
	String Result;
	PInstruction FunctionLabel;

	FunctionLabel = Code - 1;
	if (TOperationCode(FunctionLabel->id) == opFunction)
	{
		Result = Instance->Script->Strings->GetItem(FunctionLabel->Parameter);
	}
	else 
	{
		Result = new char[100];
			sprintf(Result,"Instruction at 0x%x",
				Code);
	};
	return Result;
};

TScriptExitCode OnInstanceExecuteFunctionByHandle(TInstanceHandle hInstance,
	Dword hFunction, PDword ScriptResult)
{
	TScriptExitCode Result;
	PInstance Instance;
	PInstruction Code;

	Instance = GetJassThreadLocal()->Instances.GetItem(hInstance);

	// If the current instruction is not NULL and ForceNewRun flag was not set
	// then resume execution of a previously running code.
	// ForceNewRun is never true so ignore them?
	if (Instance->CurrentOpcode != NULL)
	{
		#ifdef DEBUG
			char vStr[100];
			sprintf(vStr,"Resuming execution of instance %d.",
				Instance->Handle);
			MessageBoxA(NULL,vStr,"Send!",MB_OK | MB_ICONWARNING);
		DebugLnEnter(vStr);
		#endif
		Result = ResumeExecution(Instance, ScriptResult);
		#ifdef DEBUG
			char vStr2[100];
			sprintf(vStr2,"ExitCode: %d",
				Integer(Result));
			MessageBoxA(NULL,vStr2,"Send!",MB_OK | MB_ICONWARNING);
		DebugLnExit(vStr2);
		#endif
	}
	else
	{
		Code = Instance->CodeTable.GetItem(hFunction);
		#ifdef DEBUG
			char vStr3[100];
			sprintf(vStr3,"Executing function ""%s"" by handle %d.",
				GetCodeName(Instance, Code),
				hFunction);
			MessageBoxA(NULL,vStr3,"Send!",MB_OK | MB_ICONWARNING);
		DebugLnEnter(vStr3);
		#endif
		Result = ExecuteCode(Instance, Code, ScriptResult);
		#ifdef DEBUG
			char vStr4[100];
			sprintf(vStr4,"ExitCode: %d; Result: %d;",
				Integer(Result),
				(*ScriptResult));
			MessageBoxA(NULL,vStr4,"Send!",MB_OK | MB_ICONWARNING);
		DebugLnExit(vStr4);
		#endif
	};
	return Result;
};

TJassStringHandle OnInstanceGetStringHandle(TInstanceHandle hInstance, PChar Value)
{
	PInstance Instance;

	Instance = GetJassThreadLocal()->Instances.GetItem(hInstance);
	return GetStringHandle(Instance, Value);
};

procedure OnEnableVarChangeEvent(PChar VariableName)
{
	Integer vvi;
	PObserver Observer;
	TProcessData Process;
	TInstance::TStringTable::TItemHandle Index;

	Observer = TGameWar3::GetInstance()->State->GetVarChangeEventPublisher(VariableName);
	std::list<TProcessData>::iterator vIt;
	for (vIt = JassEnvironment.Processes->vList->begin(); vIt != JassEnvironment.Processes->vList->end(); ++vIt)
	{
		Process = (TProcessData)(*vIt);
		Index = Process.Instance->StringTable.FindHandle(VariableName);
		if (Index != 0)
		{
			Process.VarChangeObservers.SetValue(Index,Observer);
		};
	}
};

procedure SetTransformJassCodeHandleFunction(TTransformJassCodeHandleFunction Callback)
{
	pTransformJassCodeHandle = Callback;
};

TTransformJassCodeHandleFunction GetTransformJassCodeHandleFunction()
{
	return pTransformJassCodeHandle;
};

Dword DummyTransformJassCodeHandle(TJassFunctionHandle hFunction)
{
	return hFunction;
};

procedure ResetTransformJassCodeHandleFunction()
{
	pTransformJassCodeHandle = &DummyTransformJassCodeHandle;
};

TFrameData TFrameDataPoolManager::CreateInstance(Pointer AParameter)
{
	TFrameData Result;
	Result = TFrameData::TFrameData();
	Result.Locals.SetSize(TProcessData::TProcessData((PInstance)AParameter).Instance->Script->Strings->GetLength());
	return Result;
};

procedure TFrameDataPoolManager::ClearInstance(TFrameData AInstance, Pointer AParameter)
{
	PScriptData Variable;

	Integer vvi;

		for (vvi = 0; vvi <= AInstance.Locals.GetSize() - 1; vvi++)
		{
			Variable = AInstance.Locals.GetValue(vvi);
			#ifdef VANULLLA_COMPATIBILITY
			// Let them leak
			switch (Variable->_Type)
			{
				case stHandle:
					if (!Variable->IsArgument)
						Variable->_Type = stInteger;
					break;
				case stArrayOfHandle:
					Variable->_Type = stArrayOfInteger;
					break;
			};
			#endif

			TProcessData((PInstance)AParameter).ScriptDataPool.FreeItem(Variable);
		};

		AInstance.Locals.Clear();
};

procedure TFrameDataPoolManager::DestroyInstance(TFrameData AInstance,
	Pointer AParameter)
{
	AInstance.~TFrameData();
};

PJassArray TJassArrayPoolManager::CreateInstance(Pointer AParameter)
{
	Integer vvi;
	PJassArray Result;
	Result = new TJassArray;
	Result->TJassArray::TJassArray();
	Result->List.SetLength(JASS_MAX_ARRAY_SIZE);
	for (vvi = 0; vvi <= Result->List.GetLength() - 1; vvi++)
	{
		Result->List.SetItem(vvi,0);
	}
	return Result;
};

procedure TJassArrayPoolManager::ClearInstance(PJassArray AInstance,
	Pointer AParameter)
{
		// There is no way to handle references due to lack of a type information
	Integer vvi;
	for (vvi = 0; vvi <= AInstance->List.GetLength() - 1; vvi++)
	{
		AInstance->List.SetItem(vvi,0);
	}
};

procedure TJassArrayPoolManager::DestroyInstance(PJassArray AInstance,
	Pointer AParameter)
{
	AInstance->~TJassArray();
	delete AInstance;
};

PScriptData TScriptDataPoolManager::CreateInstance(Pointer AParameter)
{
	PScriptData Result;
	Result = new TScriptData;
	Result->TScriptData::TScriptData();
	return Result;
};

procedure TScriptDataPoolManager::ClearInstance(PScriptData AInstance,
	Pointer AParameter)
{
	ClearScriptData(
		PInstance(AParameter),
		*(AInstance),
		ctVariable
	);
	AInstance->IsArgument = FALSE;
};

procedure TScriptDataPoolManager::DestroyInstance(PScriptData AInstance,
	Pointer AParameter)
{
	AInstance->~TScriptData();
	delete AInstance;
};

PFrame TFramePoolManager::CreateInstance(Pointer AParameter)
{
	PFrame Result;
	Result = new TFrame;
	Result->TFrame::TFrame();
	return Result;
};

procedure TFramePoolManager::ClearInstance(PFrame AInstance,
	Pointer AParameter)
{
	AInstance->Node.Clear();

		PopFrameValues(
			PInstance(AParameter),
			AInstance,
			AInstance->Stack.Length
		);

		// A variables are owned by a data table, so dont free them here.
		AInstance->Locals.Clear();
		AInstance->IsFirstFrame = FALSE;
};

procedure TFramePoolManager::DestroyInstance(PFrame AInstance,
	Pointer AParameter)
{
	AInstance->~TFrame();
	delete AInstance;
};

TNativeData::TNativeData()
{
	//
};

TNativeData::TNativeData(const TNativeFunc ANative)
{
	TNativeFunctionSignature ParsedSignature;
	Integer vvi;

	if (ParseNativeFunctionSignature(
		ANative.Signature,
		ParsedSignature,
		FALSE
	))
	{
		ArgumentsType = new TScriptDataType[sizeof(*ParsedSignature.Arguments)/sizeof(ParsedSignature.Arguments[0])];

			for (vvi = 0; vvi <= sizeof(*ParsedSignature.Arguments)/sizeof(ParsedSignature.Arguments[0]) - 1; vvi++)
			{
				if (!GetScriptDataTypeByLetter(
					ParsedSignature.Arguments[vvi].TypeId,
					ArgumentsType[vvi]
				))
				{
					ArgumentsType[vvi] = stVoid;
				};
			};
	}
	else
	{
			char vStr[100];
			sprintf(vStr,"Failed to parse signature of native function ""%s%s"".",
				ANative.HashKeyData.Key,
				ANative.Signature);
			MessageBoxA(NULL,vStr,"Error!",MB_OK | MB_ICONWARNING);
	};
};

//template <class TThreadData>
TThreads::TThreads()
{

};

TProcessData::TProcessData()
{
	//
};

TProcessData::TProcessData(PInstance AInstance)
{
	Instance = AInstance;
	Threads->Create();
	ScriptDataPool.TPool::TPool(Pointer(Instance));
	JassArrayPool.TPool::TPool();
	FramePool.TPool::TPool(AInstance);
	FrameDataPool.TPool::TPool(Pointer(this));
	Natives.TDataTable::TDataTable();
	NativesData.TDataTable::TDataTable();
	Functions.TDataTable::TDataTable();
	Globals.TDataTable::TDataTable();
	MyStringStorage.THandleTableM::THandleTableM();
	VarChangeObservers.TDataTable::TDataTable();
};

TProcessData::~TProcessData()
{
	Integer vvi;
	PScriptData Variable;

	Threads->Destroy();
	ScriptDataPool.~TPool();
	JassArrayPool.~TPool();
	FramePool.~TPool();
	FrameDataPool.~TPool();
	Natives.~TDataTable();
	NativesData.~TDataTable();
	Functions.~TDataTable();

	
	for (vvi = 0; vvi <= Globals.GetSize(); vvi++)
	{
		Variable = Globals.GetValue(vvi);
		Variable->~TScriptData();
		delete Variable;
	};
	Globals.~TDataTable();

	MyStringStorage.THandleTableM<String,TValueComparator<String>>::~THandleTableM<String,TValueComparator<String>>();

	//inherited Destroy;
};

Boolean TProcessData::operator== (const TProcessData &b)
{
	return this == &b;
};

Boolean TProcessData::operator!= (const TProcessData &b)
{
	return this != &b;
};

Boolean TThreadData::operator== (const TThreadData &b)
{
	return this == &b;
};

Boolean TThreadData::operator!= (const TThreadData &b)
{
	return this != &b;
};

TFrameData::TFrameData()
{
	Locals.TDataTable::TDataTable();
};

procedure TFrameData::Clear()
{
	Locals.Clear();
};

TFrameData::~TFrameData()
{
	Locals.~TDataTable();

	//inherited Destroy;
};

procedure TThreadData::SetProcess(TProcessData* AProcess)
{
	if (FProcess != NULL)
	{
		FProcess.Threads->Remove(*this);
	}

	FProcess = *AProcess;

	if (FProcess != NULL)
	{
		FProcess.Threads->Add(*this);
	}
};

procedure TThreadData::ClearFrames()
{
	TFrameData FrameData;

	std::list<TFrameData>::iterator vIt;
	for (vIt = Frames.vList->begin(); vIt != Frames.vList->end(); ++vIt)
	{
		FrameData = *vIt;
		FProcess.FrameDataPool.FreeItem(FrameData);
	}

	Frames.Clear();
};

//template <class TFrameData>
TFrames::TFrames()
{

};

//template <class TFrameData>
TFrames::~TFrames()
{

};

TThreadData::TThreadData()
{
	FProcess = NULL;
	Frames.Create();
};

TThreadData::TThreadData(TProcessData* AProcess)
{
	FProcess = NULL;
	FProcess = *AProcess;
	Frames.Create();
};

TThreadData::~TThreadData()
{
	ClearFrames();
	Frames.~TFrames();

	FProcess = NULL;

	//inherited Destroy;
};

TProcesses::TProcesses()
{
};

TProcesses::~TProcesses()
{
};

TEnvironmentData::TEnvironmentData()
{
	Processes = new TProcesses();
	ThreadTable = new TThreadTable();
	(*Processes).Create();
	ThreadTable->THandleTableM::THandleTableM();
};

TEnvironmentData::~TEnvironmentData()
{
	(*Processes).Destroy();
	ThreadTable->THandleTableM<Pointer,TValueComparator<Pointer>>::~THandleTableM<Pointer,TValueComparator<Pointer>>();

	delete Processes;
	delete ThreadTable;
	//inherited Destroy;
};

void myjassvm_initialization()
{
	ResetTransformJassCodeHandleFunction();
	//JassEnvironment.TEnvironmentData::TEnvironmentData();
}

void myjassvm_finalization()
{
	JassEnvironment.~TEnvironmentData();
}